-- SG -- 
local SpDamageUtil = require("components/spdamageutil")

local function DoAOE(inst, target, dammult, rr, nodefatk)
    if not target or not target:IsValid() then return end 

    local pos =  target:GetPosition()
    local x, y, z = target.Transform:GetWorldPosition()
    local radius = rr or 3
    local AOE_RANGE_PADDING = 3
    local MUST_TAGS = { "_combat"}
    local CANT_TAGS = { "INLIMBO", "wall",  "DECOR", "FX",
    "notarget", "noattack", "playerghost", "player", "companion", "moonstorm_static" }
    local weapon = inst.components.inventory:GetEquippedItem(EQUIPSLOTS.HANDS)
    local fx = SpawnPrefab("i26bfx1")
    fx.Transform:SetPosition(x, y, z)
    fx.AnimState:SetScale(radius/2, radius/2)

    if weapon and weapon.components.i26egotool and weapon.components.i26egotool.aw_worktype ~= 0 then
        weapon.components.i26egotool:DoAoeWork(inst, x, z, 3)
    end

    inst.components.combat.ignorehitrange = true
    for k,v in pairs(TheSim:FindEntities(x, y, z, radius + AOE_RANGE_PADDING, MUST_TAGS, CANT_TAGS)) do
        if v:IsValid() 
        and not v:IsInLimbo()
        and v.components.health ~= nil 
        and not v.components.health:IsDead()
        and v.components.combat ~= nil then
            local range = v:GetPhysicsRadius(0) + radius
            if distsq(pos, v:GetPosition()) <= range * range and 
            (v.components.follower == nil or v.components.follower.leader == nil or not v.components.follower.leader:HasTag("player")) then    
                if nodefatk then
                    local vdmg, spvdmg = inst.components.combat:CalcDamage(v, weapon, 1.5)
                    if spvdmg ~= nil then
                        vdmg = vdmg + SpDamageUtil.CalcTotalDamage(spvdmg)
                    end
                    vdmg = vdmg * math.max(1, v.components.combat.externaldamagetakenmultipliers:Get()) * dammult
                    v.components.health:DoDelta(-vdmg,nil,nil,false,nil,true)
    
                    if v.components.health:IsDead() then
                        inst:PushEvent("killed", { victim = v, attacker = inst })
                    end

                    if v and v:IsValid() then
                        if v.components.combat and v.components.combat.onkilledbyother ~= nil then
                            v.components.combat.onkilledbyother(v, inst)
                        end
                        v:PushEvent("attacked", 
                        {attacker = inst, 
                        damage = vdmg, 
                        damageresolved = 0, 
                        original_damage = vdmg, 
                        weapon = weapon})
                    end
                else
                    inst.components.combat:DoAttack(v, weapon, nil, nil, dammult)
                end
            end
        end
    end
    inst.components.combat.ignorehitrange = false

end
local i26superatk = State{  --pt
    name = "i26superatk",
    tags = { "attack", "notalking", "abouttoattack" },
    onenter = function(inst)
        local equip = inst.components.inventory:GetEquippedItem(EQUIPSLOTS.HANDS)
        if inst.components.combat:InCooldown() or not equip then
            inst.sg:RemoveStateTag("abouttoattack")
            inst:ClearBufferedAction()
            inst.sg:GoToState("idle", true)
            return
        end
        local buffaction = inst:GetBufferedAction()
        local target = buffaction ~= nil and buffaction.target or nil
        inst.sg.statemem.action = inst:GetBufferedAction()

        inst.components.combat:SetTarget(target)
        inst.components.combat:StartAttack()
        inst.components.locomotor:Stop()

        --------------
        local cooldown = inst.components.combat.min_attack_period

        if inst.i27pttt ~= nil then
            inst.sg.statemem.i27fast = true
            inst.AnimState:PlayAnimation("chop_loop", false)
            cooldown = math.max(cooldown, 20 * FRAMES)
            inst.AnimState:SetDeltaTimeMultiplier(.6)
        else
            inst.AnimState:PlayAnimation("chop_pre")
            inst.AnimState:PushAnimation("chop_loop", false)
            cooldown = math.max(cooldown, 24 * FRAMES)
            inst.AnimState:SetDeltaTimeMultiplier(.8)
        end
        
        
        
        inst.sg:SetTimeout(cooldown)

        if target ~= nil then
            inst.components.combat:BattleCry()
            if target:IsValid() then
                inst:FacePoint(target:GetPosition())
                inst.sg.statemem.attacktarget = target
                inst.sg.statemem.retarget = target
            end
        end
    end,

    timeline =
    {
        TimeEvent(12 * FRAMES, function(inst)   --pt
            if inst.sg.statemem.i27fast then
                inst:PerformBufferedAction()
                inst.sg:RemoveStateTag("abouttoattack")

                if inst.i27pttt ~= nil then
                    inst.i27pttt:Cancel()
                    inst.i27pttt = nil
                end
                inst.i27pttt = inst:DoTaskInTime(.5, function()
                    inst.i27pttt = nil
                end)
            end

        end),
        TimeEvent(18 * FRAMES, function(inst)   --pt
            if not inst.sg.statemem.i27fast then
                inst:PerformBufferedAction()
                inst.sg:RemoveStateTag("abouttoattack")
                if inst.i27pttt ~= nil then
                    inst.i27pttt:Cancel()
                    inst.i27pttt = nil
                end
                inst.i27pttt = inst:DoTaskInTime(.5, function()
                    inst.i27pttt = nil
                end)
            end

        end),
    },


    ontimeout = function(inst)
        inst.sg:RemoveStateTag("attack")
        inst.sg:AddStateTag("idle") 
        inst.sg:GoToState("idle", true)
    end,

    events =
    {
        EventHandler("equip", function(inst) inst.sg:GoToState("idle") end),
        EventHandler("unequip", function(inst) inst.sg:GoToState("idle") end),
        EventHandler("animqueueover", function(inst)
            if inst.AnimState:AnimDone() then
                inst.sg:GoToState("idle")
            end
        end),
    },

    onexit = function(inst)
        inst:ClearBufferedAction()
        inst.AnimState:SetDeltaTimeMultiplier(1)
        inst.components.combat:SetTarget(nil)
        if inst.sg:HasStateTag("abouttoattack") then
            inst.components.combat:CancelAttack()
        end
    end,
}

AddStategraphState("wilson", i26superatk)
AddStategraphState("wilson_client", State{  --pt
    name = "i26superatk",
    tags = { "attack", "notalking", "abouttoattack", "autopredict" },
    onenter = function(inst)

        local equip = inst.replica.inventory:GetEquippedItem(EQUIPSLOTS.HANDS)
        local combat = inst.replica.combat
        if combat:InCooldown() then
            inst.sg:RemoveStateTag("abouttoattack")
            inst:ClearBufferedAction()
            inst.sg:GoToState("idle", true)
            return
        end
        local buffaction = inst:GetBufferedAction()
        local target = buffaction ~= nil and buffaction.target or nil
        inst.sg.statemem.action = inst:GetBufferedAction()

        combat:StartAttack()
        inst.components.locomotor:Stop()

        --------------
        local cooldown = combat:MinAttackPeriod()

        if inst.i27pttt ~= nil then
            inst.sg.statemem.i27fast = true
            inst.AnimState:PlayAnimation("chop_loop", false)
            cooldown = math.max(cooldown, 20 * FRAMES)
            inst.AnimState:SetDeltaTimeMultiplier(.6)
        else
            inst.AnimState:PlayAnimation("chop_pre")
            inst.AnimState:PushAnimation("chop_loop", false)
            cooldown = math.max(cooldown, 24 * FRAMES)
            inst.AnimState:SetDeltaTimeMultiplier(.8)
        end
        
        
        local buffaction = inst:GetBufferedAction()
        if buffaction ~= nil then
            inst:PerformPreviewBufferedAction()

            if buffaction.target ~= nil and buffaction.target:IsValid() then
                inst:FacePoint(buffaction.target:GetPosition())
                inst.sg.statemem.attacktarget = buffaction.target
                inst.sg.statemem.retarget = buffaction.target
            end
        end

        if cooldown > 0 then
            inst.sg:SetTimeout(cooldown)
        end
    end,

    timeline =
    {
        TimeEvent(13 * FRAMES, function(inst)   --pt
            if inst.sg.statemem.i27fast then
                inst:ClearBufferedAction()
                inst.sg:RemoveStateTag("abouttoattack")

                if inst.i27pttt ~= nil then
                    inst.i27pttt:Cancel()
                    inst.i27pttt = nil
                end
                inst.i27pttt = inst:DoTaskInTime(.5, function()
                    inst.i27pttt = nil
                end)
            end

        end),
        TimeEvent(18 * FRAMES, function(inst)   --pt
            if not inst.sg.statemem.i27fast then
                inst:ClearBufferedAction()
                inst.sg:RemoveStateTag("abouttoattack")
                if inst.i27pttt ~= nil then
                    inst.i27pttt:Cancel()
                    inst.i27pttt = nil
                end
                inst.i27pttt = inst:DoTaskInTime(.5, function()
                    inst.i27pttt = nil
                end)
            end

        end),
    },


    ontimeout = function(inst)
        inst.sg:RemoveStateTag("attack")
        inst.sg:AddStateTag("idle") 
        inst.sg:GoToState("idle", true)
    end,

    events =
    {
        EventHandler("equip", function(inst) inst.sg:GoToState("idle") end),
        EventHandler("unequip", function(inst) inst.sg:GoToState("idle") end),
        EventHandler("animqueueover", function(inst)
            if inst.AnimState:AnimDone() then
                inst.sg:GoToState("idle")
            end
        end),
    },

    onexit = function(inst)

        inst.AnimState:SetDeltaTimeMultiplier(1)
        if inst.sg:HasStateTag("abouttoattack") then
            inst.replica.combat:CancelAttack()
        end
    end,
})

local i26superatk2 = State{ --强化攻击 --范围
    name = "i26superatk2",
    tags = { "attack", "notalking", "abouttoattack", "nointerrupt", "pausepredict"  },
    onenter = function(inst)
        inst.components.i26skill:EnBt(1)
        if inst.components.i26skill.dzatk then
            --inst:PushEvent("i26s3hit")
            inst.i26s3hit:push()
        end
        local equip = inst.components.inventory:GetEquippedItem(EQUIPSLOTS.HANDS)
        if inst.components.combat:InCooldown() or not equip then
            inst.sg:RemoveStateTag("abouttoattack")
            inst:ClearBufferedAction()
            inst.sg:GoToState("idle", true)
            return
        end
        local buffaction = inst:GetBufferedAction()
        local target = buffaction ~= nil and buffaction.target or nil
        inst.sg.statemem.weapon = equip

        inst.components.combat:SetTarget(target)
        inst.components.combat:StartAttack()
        inst.components.locomotor:Stop()
        local cooldown = inst.components.combat.min_attack_period
        
            inst.AnimState:SetDeltaTimeMultiplier(.9)
            -- inst.AnimState:PlayAnimation("chop_pre")
            -- inst.AnimState:PushAnimation("chop_loop", false)
            inst.AnimState:PlayAnimation("atk_prop_pre")
            inst.AnimState:PushAnimation("atk_prop")

            cooldown = math.max(cooldown, 17 * FRAMES)

        inst.sg:SetTimeout(cooldown)

        if target ~= nil then
            inst.components.combat:BattleCry()
            if target:IsValid() then
                inst:FacePoint(target:GetPosition())
                inst.sg.statemem.attacktarget = target
                inst.sg.statemem.retarget = target
            end
        end
    end,

    timeline =
    {
        TimeEvent(11 * FRAMES, function(inst)   --pt
            ---mod名字inst:PerformBufferedAction()
            local target = inst.sg.statemem.attacktarget
            local i26skill = inst.components.i26skill
            if i26skill:IsAOE() then
                DoAOE(inst, inst.sg.statemem.attacktarget, i26skill:GetMult())
            elseif target and target:IsValid() then
                inst.components.combat:DoAttack(target, nil, nil, nil, i26skill:GetMult())
            end
            inst.sg:RemoveStateTag("abouttoattack")
            inst:ClearBufferedAction()
            inst.AnimState:SetDeltaTimeMultiplier(1)
            i26skill:CancelSuperAtk(inst.sg.statemem.weapon)
            
        end),
    },

    ontimeout = function(inst)
        inst.sg:RemoveStateTag("attack")
        inst.sg:AddStateTag("idle")
        inst.sg:GoToState("idle")
    end,

    events =
    {
        EventHandler("equip", function(inst) inst.sg:GoToState("idle") end),
        EventHandler("unequip", function(inst) inst.sg:GoToState("idle") end),
        EventHandler("animqueueover", function(inst)
            if inst.AnimState:AnimDone() then
                inst.sg:GoToState("idle")
            end
        end),
    },

    onexit = function(inst)
        inst.AnimState:SetDeltaTimeMultiplier(1)
        inst.components.combat:SetTarget(nil)
        if inst.sg:HasStateTag("abouttoattack") then
            inst.components.combat:CancelAttack()
        end
    end,
}

AddStategraphState("wilson", i26superatk2)
AddStategraphState("wilson_client", State{
    name = "i26superatk2",
    tags = { "attack", "notalking", "abouttoattack", "nointerrupt"  },
    onenter = function(inst)
        local combat = inst.replica.combat
        local equip = inst.replica.inventory:GetEquippedItem(EQUIPSLOTS.HANDS)
        if combat:InCooldown() or not equip then
            inst.sg:RemoveStateTag("abouttoattack")
            inst:ClearBufferedAction()
            inst.sg:GoToState("idle", true)
            return
        end
        local buffaction = inst:GetBufferedAction()
        local target = buffaction ~= nil and buffaction.target or nil
        inst.sg.statemem.weapon = equip

        combat:SetTarget(target)
        combat:StartAttack()
        inst.components.locomotor:Stop()
        local cooldown = 17 * FRAMES
        
            inst.AnimState:SetDeltaTimeMultiplier(.9)
            -- inst.AnimState:PlayAnimation("chop_pre")
            -- inst.AnimState:PushAnimation("chop_loop", false)
            inst.AnimState:PlayAnimation("atk_prop_pre")
            inst.AnimState:PushAnimation("atk_prop")


        local buffaction = inst:GetBufferedAction()
        if buffaction ~= nil then
            inst:PerformPreviewBufferedAction()

            if buffaction.target ~= nil and buffaction.target:IsValid() then
                inst:FacePoint(buffaction.target:GetPosition())
                inst.sg.statemem.attacktarget = buffaction.target
                inst.sg.statemem.retarget = buffaction.target
            end
        end

        if cooldown > 0 then
            inst.sg:SetTimeout(cooldown)
        end
    end,

    timeline =
    {
        TimeEvent(11 * FRAMES, function(inst)   --pt
            inst.sg:RemoveStateTag("abouttoattack")
            inst:ClearBufferedAction()
            inst.AnimState:SetDeltaTimeMultiplier(1)
        end),
    },

    ontimeout = function(inst)
        inst.sg:RemoveStateTag("attack")
        inst.sg:AddStateTag("idle")
        inst.sg:GoToState("idle")
    end,

    events =
    {
        EventHandler("equip", function(inst) inst.sg:GoToState("idle") end),
        EventHandler("unequip", function(inst) inst.sg:GoToState("idle") end),
        EventHandler("animqueueover", function(inst)
            if inst.AnimState:AnimDone() then
                inst.sg:GoToState("idle")
            end
        end),
    },

    onexit = function(inst)
        inst.AnimState:SetDeltaTimeMultiplier(1)
        if inst.sg:HasStateTag("abouttoattack") then
            inst.replica.combat:CancelAttack()
        end
    end,
})

local i26sgskill1 = State{  --飞铗震赫
    name = "i26sgskill1",
    tags = { "doing", "busy", "nointerrupt", "nomorph" },

    onenter = function(inst, data)
        if data and data.pos then
            inst.components.locomotor:Stop()
            inst.AnimState:SetDeltaTimeMultiplier(1.4)
            inst.Physics:SetMotorVel(20, 0, 0)
            inst:ForceFacePoint(data.pos:Get())
            inst.AnimState:PlayAnimation("chop_pre")
            local weapon = inst.components.inventory:GetEquippedItem(EQUIPSLOTS.HANDS)
            if weapon and weapon.components.i26egotool then
                inst.sg.statemem.weapon = weapon
                weapon.components.i26egotool:EnableLunacy(true, "skill1")
            end
            
            return
        end
        inst.sg:GoToState("idle")
    end,

    events =
    {
        EventHandler("animover", function(inst)
            if inst.AnimState:AnimDone() then
                inst.sg:GoToState("i26sgskill1_pst")
            end
        end),
    },

    onexit = function(inst)
        inst.components.locomotor:Stop()
        inst.AnimState:SetDeltaTimeMultiplier(1)

        local weapon = inst.sg.statemem.weapon
        if weapon and weapon:IsValid() then
            weapon.components.i26egotool:EnableLunacy(false, "skill1")
        end
    end,
}
local i26sgskill1_pst = State{
    name = "i26sgskill1_pst",
    tags = { "aoe", "doing", "busy", "nopredict", "nomorph", "nointerrupt" },

    onenter = function(inst, data)
        inst.AnimState:PlayAnimation("lunge_pst")
        inst.SoundEmitter:PlaySound("dontstarve/common/lava_arena/fireball")
        local weapon = inst.components.inventory:GetEquippedItem(EQUIPSLOTS.HANDS)
        if weapon and weapon.components.i26egotool then
            inst.sg.statemem.weapon = weapon
            weapon.components.i26egotool:EnableLunacy(true, "skill1")
        end
    end,

    timeline =
    {
        TimeEvent(2 * FRAMES, function(inst)
            inst.AnimState:PlayAnimation("atk")
            inst.SoundEmitter:PlaySound("dontstarve/wilson/attack_weapon")
            DoAOE(inst, inst, 1.5, 5, true)
        end),
        TimeEvent(6 * FRAMES, function(inst)
            inst.sg:RemoveStateTag("busy")
        end),
    },

    events =
    {
        EventHandler("animover", function(inst)
            if inst.AnimState:AnimDone() then
                inst.sg:GoToState("idle")
            end
        end),
    },

    onexit = function(inst)
        local weapon = inst.sg.statemem.weapon
        if weapon and weapon:IsValid() then
            weapon.components.i26egotool:EnableLunacy(false, "skill1")
        end
    end,
}
AddStategraphState("wilson", i26sgskill1)
AddStategraphState("wilson", i26sgskill1_pst)

local i26sgskill2 = State{  --剑为地纪，刃惊天宗
    name = "i26sgskill2",
    tags = { "busy", "no_stun", "nointerrupt"},
    onenter = function(inst, data)
        inst.components.locomotor:Stop()
        inst.components.i26skill.superdef = true
        inst.components.i26skill:EnBt()
        inst.AnimState:PlayAnimation("parry_loop")

        local weapon = inst.components.inventory:GetEquippedItem(EQUIPSLOTS.HANDS)
        if weapon and weapon.components.i26egotool then
            inst.sg.statemem.weapon = weapon
            weapon.components.i26egotool:EnableLunacy(true, "skill2")
        end

        inst.sg:SetTimeout(2)
    end,
    events =
    {
        EventHandler("i26attacked", function(inst)
            inst.sg.statemem.tfok = true
            --inst.sg:GoToState("idle",true)
            if inst.components.i26skill.DD_TOPER then
                inst.AnimState:PlayAnimation("parryblock")
                inst.sg:RemoveStateTag("busy")
            else
                inst.sg:GoToState("idle",true)
            end
        end),
    },
    ontimeout = function(inst)
        if not inst.sg.statemem.tfok then
            inst.components.i26skill:DoSuper2()
        end
        inst.sg:GoToState("idle",true)
    end,
    onexit = function(inst)
        inst.components.i26skill.superdef = false
        local weapon = inst.sg.statemem.weapon
        if weapon and weapon:IsValid() then
            weapon.components.i26egotool:EnableLunacy(false, "skill2")
        end
        if inst.sg.statemem.tfok then
            inst.components.i26skill:EnBt(1)
        else
            inst.components.i26skill:UnBt()
        end
    end,
}
AddStategraphState("wilson", i26sgskill2)

local throwsg = State{ --扔出去
    name = "i26tlaotie",
    tags = { "notalking", "nointerrupt", "busy", "pausepredict"  },    --, "nopredict"
    onenter = function(inst)
        if inst.components.i26skill then
            inst.components.i26skill:EnBt(1)
        else
            inst.sg:GoToState("idle")
        end
        inst.components.locomotor:Stop()

        inst.AnimState:SetDeltaTimeMultiplier(1)
        inst.AnimState:PlayAnimation("atk_prop_pre")
    end,

    events =
    {
        EventHandler("animqueueover", function(inst)
            if inst.AnimState:AnimDone() then
                inst.sg:GoToState("i26tlaotie_pst")
            end
        end),
    },

    onexit = function(inst)
        inst.SoundEmitter:PlaySound("dontstarve/wilson/attack_whoosh")
        inst:PerformBufferedAction()
        inst.AnimState:SetDeltaTimeMultiplier(1)
        inst:ClearBufferedAction()
    end,
}
local throwsg_client = State{ 
    name = "i26tlaotie",
    tags = { "notalking", "nointerrupt", "busy", "pausepredict"  },
    onenter = function(inst)
        if inst.components.i26skill then
            inst.components.i26skill:EnBt(1)
        end
        
        inst.components.locomotor:Stop()

        inst.AnimState:SetDeltaTimeMultiplier(1)
        inst.AnimState:PlayAnimation("atk_prop_pre")
    end,

    timeline =
    {
        TimeEvent(6 * FRAMES, function(inst)
            inst.SoundEmitter:PlaySound("dontstarve/wilson/attack_whoosh")
            inst:PerformPreviewBufferedAction()
        end),
        TimeEvent(17 * FRAMES, function(inst)
            inst.sg:GoToState("idle", true)
        end),
    },

    events =
    {
        EventHandler("animqueueover", function(inst)
            if inst.AnimState:AnimDone() then
                inst.sg:GoToState("i26tlaotie_pst")
            end
        end),
    },

    onexit = function(inst)
        inst:ClearBufferedAction()
        inst.AnimState:SetDeltaTimeMultiplier(1)
    end,
}

local throwsg_pst = State{ --扔出去
    name = "i26tlaotie_pst",
    tags = { "notalking",  "nointerrupt",   },
    onenter = function(inst)
        inst.components.locomotor:Stop()
        if inst.components.i26skill then
            inst.components.i26skill:EnBt(3)
        end
        
        inst.AnimState:PlayAnimation("atk_prop")
    end,

    timeline =
    {
        TimeEvent(12 * FRAMES, function(inst)
            inst:ClearBufferedAction()
            inst.sg:GoToState("idle", true)
        end),
    },

    events =
    {
        EventHandler("animqueueover", function(inst)
            if inst.AnimState:AnimDone() then
                inst.sg:GoToState("idle")
            end
        end),
    },

    onexit = function(inst)
        inst.AnimState:SetDeltaTimeMultiplier(1)
    end,
}
AddStategraphState("wilson", throwsg)
AddStategraphState("wilson_client", throwsg_client)
AddStategraphState("wilson", throwsg_pst)
AddStategraphState("wilson_client", throwsg_pst)

